Skip to content

Add support for PostgreSQL-style CREATE TYPE#281

Merged
jongleb merged 2 commits into
ygrek:masterfrom
Niols:create-type
Jul 3, 2026
Merged

Add support for PostgreSQL-style CREATE TYPE#281
jongleb merged 2 commits into
ygrek:masterfrom
Niols:create-type

Conversation

@Niols

@Niols Niols commented Jun 5, 2026

Copy link
Copy Markdown
Contributor

This PR builds on top of #276. Although they are in practice independent changes, they both aim at improving PostgreSQL support in sqlgg, and I have added the new commit on top of the previous one. When reviewing, I recommend making sure to only read the last commit, or, even better, to wait before #276 is merged before reviewing anything here.

I hope the change is welcome; please let me know if there is anything that you would want me to do differently, I will be happy to update my PR.

Target

PostgreSQL allows declaring a named type with CREATE TYPE, then referring to it elsewhere. This is most useful for enums:

CREATE TYPE "color" AS ENUM ('red', 'green', 'blue');

CREATE TABLE "shirt" (
  "id" INT NOT NULL,
  "color" "color" NOT NULL
);

SELECT "id" FROM "shirt" WHERE "color" = 'red';

and in fact necessary, as inline ENUM is just not accepted. Unlike MySQL's inline ENUM(...) columns, this is a separate statement that introduces a name to be looked up later. It cannot be handled at the leaf of the type grammar alone: it requires a registry that one statement populates and a later statement consults.

Change

The main change is the new module lib/types.ml, similar to lib/tables.ml (albeit much simpler) but for types. Encountering a new CREATE TYPE adds to a global registry found in that module; the counterpart DROP TYPE removes from it, and we look types up during analysis of CREATE TABLE. The rest of the change is what one expects, namely adding those syntaxes to the grammar and following the new types everywhere. I add some Cram-style tests for the feature in test/cram/create_type.t.

Limitations

  • CreateType / DropType carry no source position, so the dialect-mismatch error reads ... at with an empty tail.
  • DROP TYPE IF EXISTS parses, but the flag is discarded, so it still errors on missing names. This matches the pre-existing behaviour of DROP TABLE IF EXISTS.
  • PostgreSQL would actually reject DROP TYPE foo if foo is used in a table somewhere; I did not add such checks in this PR.
  • Only AS ENUM (...) is supported; the other PostgreSQL forms (composite, range, base, shell) are not.
  • The similar CREATE DOMAIN statement, which allows aliasing existing types with some extra constraints is not supported.

Niols and others added 2 commits July 3, 2026 08:29
@jongleb

jongleb commented Jul 3, 2026

Copy link
Copy Markdown
Collaborator

Rebased this on master, Made TYPE a real keyword instead of

| DROP kw=IDENT if_exists? name=IDENT
     { if String.lowercase_ascii kw <> "type" then failwith ("expected TYPE after DROP, got " ^ kw);
       DropType name }

It is an unreserved keyword. https://github.com/postgres/postgres/blob/REL_18_0/src/backend/parser/gram.y

Performing the replacement in the parser turned out it's noise, but I decided it was the better approach.

And renamed types.ml to user_types.ml to avoid confusion with the type inference

@jongleb jongleb merged commit 8b2de81 into ygrek:master Jul 3, 2026
1 check was pending
@jongleb

jongleb commented Jul 3, 2026

Copy link
Copy Markdown
Collaborator

Thank you ! I merged

@Niols

Niols commented Jul 3, 2026

Copy link
Copy Markdown
Contributor Author

Nice, thank you :-)

@Niols Niols deleted the create-type branch July 4, 2026 09:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants